package com.appistry.api.rest.impl;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.appistry.api.rest.RestConnection;
import com.appistry.api.rest.domain.RestResponse;
import com.appistry.api.rest.exception.RestException;

public abstract class AbstractRestConnection implements RestConnection {
	private static final Log LOG = LogFactory.getLog(AbstractRestConnection.class);

	public RestResponse delete(String path) throws RestException {
		try {
			DeleteMethod method = new DeleteMethod(path); 
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	public RestResponse get(String path) throws RestException {
		try {
			GetMethod method = new GetMethod(path);
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	public RestResponse head(String path) throws RestException {
		HeadMethod headMethod = null;

		try {
			headMethod = new HeadMethod(path);
			return execute(headMethod);
		} catch (IOException e) {
			throw new RestException(e);
		} finally {
			if (headMethod != null) {
				headMethod.releaseConnection();
			}
		}
	}

	public RestResponse move(String path, String dest) throws RestException {
		try {
			PostMethod method = new PostMethod(path);
			method.setRequestHeader("X-Method-Override", "MOVE");
			method.setRequestHeader("destination", dest);
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		}
	}

	public RestResponse post(String path, InputStream content) throws RestException {
		try {
			PostMethod method = new PostMethod(path);
			method.setRequestEntity(new InputStreamRequestEntity(content));
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		} finally {
			close(content);
		}
	}

	public RestResponse put(String path, InputStream content) throws RestException {
		try {
			PutMethod method = new PutMethod(path);			
			method.setRequestEntity(new InputStreamRequestEntity(content));			
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		} finally {
			close(content);
		}
	}

	public RestResponse put(String path, long length, InputStream content) throws RestException {
		try {
			PutMethod method = new PutMethod(path);
			method.setRequestEntity(new InputStreamRequestEntity(content, length));		
			RestResponse response = execute(method);
			
			return response;
		} catch (IOException e) {
			throw new RestException(e);
		} finally {
			close(content);
		}
	}
	
	/**
	 * Helper method to close input stream
	 * @param is
	 */
	protected void close(InputStream is) {
		if (is != null) {
			try {
				is.close();
			} catch (IOException e) {
				LOG.warn("Unable to close InputStream!", e);
			}
		}
	}
	
	/**
	 * Executes the http method and returns a RestResponse
	 * 
	 * @param method
	 * @return
	 * @throws IOException
	 */
	protected RestResponse execute(HttpMethod method) throws IOException{
		
		HttpClient client = getHttpClient();
		client.executeMethod(method);

		return new RestResponse(method);
	}

	//Abstracted out for HTTP vs HTTPS
	protected abstract HttpClient getHttpClient();
		
}
